package com.rainy.cloud.jdbc.mybatis.handler;

import com.rainy.cloud.jdbc.mybatis.properties.JdbcProperties;
import com.rainy.cloud.jdbc.mybatis.properties.MybatisProperties;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;
import tk.mybatis.mapper.common.BaseMapper;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;

import javax.sql.DataSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class MybatisHandler {

    public DataSource dataSource(JdbcProperties jdbcProperties){
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(jdbcProperties.getDriverClassName());
        dataSource.setJdbcUrl(jdbcProperties.getJdbcUrl());
        dataSource.setUsername(jdbcProperties.getUsername());
        dataSource.setPassword(jdbcProperties.getPassword());
        return dataSource;
    }

    public MapperScannerConfigurer mapperHelper(MybatisProperties mybatisProperties){
        MapperScannerConfigurer scan = new MapperScannerConfigurer();
        scan.setSqlSessionFactoryBeanName("sqlSessionFactory");
        scan.setBasePackage(mybatisProperties.getInterfaceScan());
        scan.setMarkerInterface(BaseMapper.class);
        return scan;
    }

    public SqlSessionFactory sqlSessionFactoryBean(JdbcProperties jdbcProperties, MybatisProperties mybatisProperties) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource(jdbcProperties));
        bean.setTypeAliasesPackage(mybatisProperties.getModelScan());
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setMapperLocations(resolver.getResources(mybatisProperties.getXmlScan()));
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public PlatformTransactionManager transactionManager(JdbcProperties jdbcProperties) {
        return new DataSourceTransactionManager(dataSource(jdbcProperties));
    }

    public TransactionInterceptor txAdvice(JdbcProperties jdbcProperties) {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(5);
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("add*", requiredTx);
        txMap.put("save*", requiredTx);
        txMap.put("insert*", requiredTx);
        txMap.put("update*", requiredTx);
        txMap.put("delete*", requiredTx);
        txMap.put("get*", readOnlyTx);
        txMap.put("query*", readOnlyTx);
        source.setNameMap( txMap );
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager(jdbcProperties), source);
        return txAdvice;
    }

    public Advisor txAdviceAdvisor(JdbcProperties jdbcProperties, MybatisProperties mybatisProperties) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(mybatisProperties.getTransactionExpress());
        return new DefaultPointcutAdvisor(pointcut, txAdvice(jdbcProperties));
    }
}
